package com.ricky.mq.client.code.handler;

import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.ricky.mq.client.Version;
import com.ricky.mq.client.code.constant.ServerCommandCode;
import com.ricky.mq.client.message.data.server.ServerBoringMessageData;
import com.ricky.mq.client.message.data.server.ServerHeartbeatCheckData;
import com.ricky.mq.client.message.data.server.ServerMsgModelData;

import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.List;

/**
 * 客户端数据处理器
 *
 * @author ricky
 * @since 2020.06.18
 */
public class ServerDataHandler {

    /**
     * 写日志的
     */
    private static Log LOG = LogFactory.get(ServerDataHandler.class);

    /**
     * 处理服务端数据
     *
     * @param serverData
     */
    public static void handler(SocketChannel socketChannel, String serverData) {

        List<String> serverDataList = decomposeMucilage(serverData);

        serverDataList.forEach((x) -> {
            if (verification(x)) {
                String commandCode = StrUtil.subWithLength(x, 16, 4);
                String data = new String(HexUtil.decodeHex(StrUtil.subWithLength(x, 20, x.length() - 28)));
                switch (commandCode) {
                    case ServerCommandCode.HEART_BEAT_CHECK:
                        WaitOrSendMsgHandler.setCallbackData(JSONUtil.toBean(data, ServerHeartbeatCheckData.class));
                        break;
                    case ServerCommandCode.CREATE_QUEUE:
                    case ServerCommandCode.CREATE_TOPIC:
                        WaitOrSendMsgHandler.setCallbackData(JSONUtil.toBean(data, ServerMsgModelData.class));
                        break;
                    case ServerCommandCode.QUEUE_MESSAGE:
                    case ServerCommandCode.TOPIC_MESSAGE:
                        WaitOrSendMsgHandler.setCallbackData(JSONUtil.toBean(data, ServerBoringMessageData.class));
                        break;
                    case ServerCommandCode.LISTENER_QUEUE_MESSAGE:
                        ListenerQueueMsgHandler.getListenerQueueMsgHandler().handler(socketChannel, data);
                        break;
                    case ServerCommandCode.LISTENER_TOPIC_MESSAGE:
                        ListenerTopicMsgHandler.getListenerTopicMsgHandler().handler(socketChannel, data);
                        break;
                    default:
                        throw new RuntimeException("无此命令：" + commandCode);
                }

            } else {
                LOG.error("clientData：" + x);
            }
        });
    }

    /**
     * 效验客户端是否合法
     *
     * @param clientData 客户端数据
     * @return
     */
    private static boolean verification(String clientData) {
        if (!StrUtil.startWith(clientData, "FADA", true)) {
            LOG.error("clientData数据不合法！报文不以FADA开头");
            return false;
        }
        if (!StrUtil.endWith(clientData, "ADAF", true)) {
            LOG.error("clientData数据不合法！报文不以ADAF结尾");
            return false;
        }

        int clientDataLength = Integer.parseInt(StrUtil.subWithLength(clientData, 4, 8), 16);
        if (clientDataLength != clientData.length()) {
            LOG.error("clientData数据长度不合法！报文长度域中长度：" + clientDataLength + ",实际长度" + clientData.length());
            return false;
        }

        String version = StrUtil.subWithLength(clientData, 12, 4);
        if (!Version.MESSAGE_VERSION.equalsIgnoreCase(version)) {
            LOG.error("客户端与服务器版本不一致！客户端版本:" + version + ",服务器版本：" + Version.MESSAGE_VERSION);
            return false;
        }

        String clientValidity = StrUtil.subWithLength(clientData, clientData.length() - 8, 4);
        String validityData = StrUtil.subWithLength(clientData, 0, clientData.length() - 8);
        int sum = 0;
        for (int i = 0; i < validityData.length(); i += 2) {
            sum += Integer.parseInt(validityData.substring(i, i + 2), 16);
        }
        sum &= 0xff;
        String validity = StrUtil.padPre(Integer.toHexString(sum), 4, "0");
        if (!clientValidity.equalsIgnoreCase(validity)) {
            LOG.error("clientData数据不合法！效验域验证不一致！");
            return false;
        }

        return true;
    }

    /**
     * 解决黏包问题
     *
     * @param serverData 数据
     * @return 分解好的数据包
     */
    public static List<String> decomposeMucilage(String serverData) {
        List<String> dataList = new ArrayList<>(8);
        do {
            int limit = Integer.parseInt(StrUtil.subWithLength(serverData, 4, 8), 16);
            String left = serverData.substring(0, limit);
            dataList.add(left);
            serverData = serverData.substring(limit);
        } while (serverData.length() > 0);
        return dataList;
    }

}
